#include "files/file_path.h"

namespace Tvimeg
{
	const char PATH_SEPARATOR = '/';
	const char EXTENSION_SEPARATOR = '.';
	const std::string ROOT_PATH("/");

	FilePath::FilePath(void)
		: path(ROOT_PATH)
	{
	}

	FilePath::FilePath(const FilePath& pathIn)
		: path(pathIn.path)
	{
	}

	FilePath::FilePath(const std::string& pathIn)
		: path(pathIn)
	{
	}

	FilePath::FilePath(const char* pathIn)
		: path(pathIn)
	{
	}

	FilePath& FilePath::operator=(const FilePath& other)
	{
		path = other.path;
		return *this;
	}

	FilePath& FilePath::operator=(const std::string& other)
	{
		path = other;
		return *this;
	}

	FilePath& FilePath::operator=(const char* other)
	{
		path = other;
		return *this;
	}

	bool FilePath::operator==(const FilePath& other) const
	{
		return path == other.path;
	}

	bool FilePath::operator==(const std::string& other) const
	{
		return path == other;
	}

	bool FilePath::operator==(const char* other) const
	{
		return path == other;
	}

	bool FilePath::operator!=(const FilePath& other) const
	{
		return path != other.path;
	}

	bool FilePath::operator!=(const std::string& other) const
	{
		return path != other;
	}

	bool FilePath::operator!=(const char* other) const
	{
		return path != other;
	}

	FilePath FilePath::operator+(const FilePath& other) const
	{
		if (other.IsAbsolute())
		{
			// if the other path is absolute it does not make sense to append,
			// so just return the other path
			return other;
		}
		else
		{
			// base the new path on this path
			FilePath newPath(*this);

			// get rid of any ..'s in the path
			size_t parentStart = 0;
			while (parentStart < other.path.length() && other.path.substr(parentStart, 2) == "..")
			{
				// get the parent folder
				newPath = newPath.GetParent();
				parentStart += 3;
			}

			if (newPath.path.length() == 0 || newPath.path[newPath.path.length() - 1] != PATH_SEPARATOR)
			{
				newPath.path += PATH_SEPARATOR;
			}

			if (other.path.length() > parentStart)
			{
				if (other.path[parentStart] == PATH_SEPARATOR)
				{
					++parentStart;
				}

				if (other.path.length() > parentStart)
				{
					newPath.path += other.path.substr(parentStart);
				}
			}

			return newPath;
		}
	}

	FilePath FilePath::operator+(const std::string& other) const
	{
		return operator+(FilePath(other));
	}

	FilePath FilePath::operator+(const char* other) const
	{
		return operator+(FilePath(other));
	}

	bool FilePath::operator<(const FilePath& other) const
	{
		return path < other.path;
	}

	bool FilePath::IsRelative(void) const
	{
		return path.length() == 0 || path[0] != PATH_SEPARATOR;
	}

	bool FilePath::IsAbsolute(void) const
	{
		return !IsRelative();
	}

	bool FilePath::IsRoot(void) const
	{
		return path == ROOT_PATH;
	}

	FilePath FilePath::GetParent(void) const
	{
		if (IsRoot() || path.length() == 0)
		{
			// already at the root, so can't get a parent from here
			return *this;
		}
		else
		{
			// we don't want to find the path separator at the end of the current path
			// but the one before that, so don't include the final character in the
			// search if it is a path separator
			size_t startPos = path.length() - 1;
			if (startPos > 0 && path[startPos] == PATH_SEPARATOR)
			{
				--startPos;
			}

			// find the location of the last path separator
			const size_t separatorPos = path.find_last_of(PATH_SEPARATOR, startPos);
			if (separatorPos != std::string::npos)
			{
				return FilePath(path.substr(0, separatorPos + 1));
			}
			else
			{
				return FilePath("");
			}
		}
	}

	const std::string& FilePath::GetPath(void) const
	{
		return path;
	}

	std::string FilePath::GetNameAndExtension(void) const
	{
		// don't start searching from the last character if it is a path separator
		size_t startPos = path.length() - 1;
		size_t subtractLength = 0;
		if (startPos > 0 && path[startPos] == PATH_SEPARATOR)
		{
			--startPos;
			++subtractLength;
		}

		// the path is empty
		if (startPos == 0)
		{
			return "";
		}

		// find the last path separator
		const size_t separatorPos = path.find_last_of(PATH_SEPARATOR, startPos);
		if (separatorPos != std::string::npos)
		{
			// copy the string from the last path separator to the end of the string
			return path.substr(separatorPos + 1, path.length() - subtractLength - separatorPos - 1);
		}
		else
		{
			// didn't find any path separator, so just copy the entire string
			return path.substr(0, path.length() - subtractLength);
		}
	}

	std::string FilePath::GetName(void) const
	{
		const std::string nameAndExtension = GetNameAndExtension();
		const size_t separatorPos = nameAndExtension.find_last_of(EXTENSION_SEPARATOR);
		if (separatorPos != std::string::npos)
		{
			return nameAndExtension.substr(0, separatorPos);
		}
		else
		{
			return nameAndExtension;
		}
	}

	std::string FilePath::GetExtension(void) const
	{
		const std::string nameAndExtension = GetNameAndExtension();
		const size_t separatorPos = nameAndExtension.find_last_of(EXTENSION_SEPARATOR);
		if (separatorPos != std::string::npos)
		{
			return nameAndExtension.substr(separatorPos + 1);
		}
		else
		{
			return "";
		}
	}
}
